package com.rpframework.module.common.url;

import com.rpframework.module.common.bottom.entity.enums.SourceType;
import com.rpframework.module.common.config.Config;
import com.rpframework.utils.Arrays;
import com.rpframework.utils.PackageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 权限拦截解释<br>
 * spring地址（URI）扫描 ，如果在类名上有次注解 方法上都加上次加上次来源,<br>
 * 如果方法上有刺注解就不以来类上注解
 */
@Component
public class SourceAuthorityExplain {

    @Autowired
    private Config config;

    private Map<String, SourceType[]> map;

    /**
     * 获取扫描的数据
     *
     * @return 数据
     */
    public Map<String, SourceType[]> getScan() {
        if (map == null) {
            map = scan(config.getControllerPackagePath());
        }
        return map;
    }

    /**
     * 匹配来源
     *
     * @param url 地址
     * @return 来源
     */
    public SourceType[] find(String url) {
        Map<String, SourceType[]> scan = getScan();
        for (Entry<String, SourceType[]> data : scan.entrySet()) {
            if (data.getKey().equals(url)) {
                return data.getValue();
            }
        }
        return null;
    }

    /**
     * 基础检测(是否存在注解)
     *
     * @param url    地址
     * @param source 来源
     * @return 是否允许访问
     */
    public boolean check(String url, SourceType source) {
        SourceType[] find = find(url);
        if (find == null) {
            return true;
        } else {
            SourceType SourceTypeOk = null;
            for (SourceType SourceType : find) {
                if (SourceType.equals(source)) {
                    SourceTypeOk = SourceType;
                    break;
                }
            }
            if (SourceTypeOk == null) {
                return false;
            }
            return true;
        }
    }

    /**
     * 扫描类
     *
     * @param pakeage 包路径
     * @return URI, 来源
     */
    public Map<String, SourceType[]> scan(String pakeage) {
        Map<String, SourceType[]> map = new HashMap<String, SourceType[]>();
        Set<Class<?>> classes = PackageUtils.scanClasses(pakeage);
        for (Class<?> clazz : classes) {
            RequestMapping classMapping = clazz.getAnnotation(RequestMapping.class);
            SourceAuthority classSourceAuthority = clazz.getAnnotation(SourceAuthority.class);
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                if (methodMapping != null) {
                    SourceAuthority sourceAuthority = method.getAnnotation(SourceAuthority.class);
                    if (sourceAuthority != null) {
                        if (classMapping != null) {
                            String[] classUri = classMapping.value();
                            String[] methodUri = methodMapping.value();
                            methodUri = Arrays.combination(classUri, methodUri);
                            for (String uri : methodUri) {
                                map.put(uri, sourceAuthority.value());
                            }
                        } else {
                            String[] value = methodMapping.value();
                            for (String uri : value) {
                                map.put(uri, sourceAuthority.value());
                            }
                        }

                    } else {
                        if (classSourceAuthority != null) {
                            if (classMapping != null) {
                                String[] classUri = classMapping.value();
                                String[] methodUri = methodMapping.value();
                                methodUri = Arrays.combination(classUri, methodUri);
                                for (String uri : methodUri) {
                                    map.put(uri, classSourceAuthority.value());
                                }
                            } else {
                                String[] value = methodMapping.value();
                                for (String uri : value) {
                                    map.put(uri, classSourceAuthority.value());
                                }
                            }
                        }
                    }
                }
            }
        }
        return map;
    }
}
